My jQuery Autocomplete Mod

Posted by Dan on Jan 12, 2007 @ 5:03 PM

On a recent project I was working on, I needed an "autocomplete" form field that could do both local data array and AJAX lookups. After doing some searching, I came across Dylan Verheul's jQuery Autcomplete plug-in.

This plug-in did a lot of what I needed, but was still missing some of the functionality I required. So, I just modified the library so it worked the way I needed it to. Here's a list of the changes/enhancements I made:

  • Supports local data array (can now use w/out AJAX).
  • Limit dropdown to XX number of results (good for limiting the results to users)
  • Autofill pre-populates text box as you type
  • New findValue() method can be used to programmatically determine if the value in the box is a valid option. (Useful for verifying the text entered is an existing value option.)
  • Dropdown options now correctly re-position themselves on each display (which means they adjust for changing to the DOM)
  • Dropdown box defaults to the width of the input field its attached to (you can manually specify a larger width as well)
  • Better emulates Windows autocomplete boxes (for example: hitting delete and retyping the same box will now bring back the dropdown menu)
  • Miscellaneous bug fixes

See a live example at http://www.pengoworks.com/workshop/jquery/autocomplete.htm.

UPDATE:
jQuery v1.1.1 has a bug in the pushStack() method that causes a weird behavior of the iFrame being populated in the text box when the [TAB] key is pressed. See the following thread on the jQuery Mailing List for more information: http://www.nabble.com/Bug-in-$()-in-jQuery-v1.1.1--t3144910.html#nabble.i8718421. You can either update your jquery.js file or pull out the latest version from the SVN.
Categories: JavaScript, HTML/ColdFusion, jQuery, Source Code

53 Comments

  • I was going crazy trying to get this to work on my site. I use floating divs with z-indexes. Because my divs had z-indexes and yours didn't, the suggestion box was appearing below the rest of the items on the site. I added this line to the ".ac_results" item in the css. "z-index: 10000;" That was a high enough number for my site. Maybe there's a "z-index: top;" that would work for every other site? I don't know.
  • Hello,

    Your plugin is great (should be the best autocomplete plugin I found), but it has a little bug when used with the latest version of jquery: iframe codes will appear in the search text box when you hit `tab` while autocompleting.

    also, i found that your codes can't be compressed with the dean edward javascript compressor...
  • @Tszming:

    This is actually a bug w/jQuery v1.1.1 and not with my code. The problem lies in the jQuery pushStack() method.

    See the following post from the jQuery Mailing List for more information:

    http://www.nabble.com/Bug-in-$()-in-jQuery-v1.1.1--t3144910.html#nabble.i8718421
  • Thanks for your good works!
  • I found bug today...

    you can replace the source with my function below, the problem of the original source is: even matchSubset is set to 0, your initialization will turn it into 1 anyway.


    jQuery.fn.autocomplete = function(url, options, data) {

        // Make sure options exists
        options = typeof(options) != 'undefined' ? options : {};

        // Set url as option
        options.url = url;
        // set some bulk local data
        options.data = ((typeof data == "object") && (data.constructor == Array)) ? data : null;

        // Set default values for required options
        options.inputClass = typeof(options.inputClass) != 'undefined' ? options.inputClass : "ac_input";
        options.resultsClass = typeof(options.resultsClass) != 'undefined' ? options.resultsClass : "ac_results";
        options.lineSeparator = typeof(options.lineSeparator) != 'undefined' ? options.lineSeparator : "\n";
        options.cellSeparator = typeof(options.cellSeparator ) != 'undefined' ? options.cellSeparator : "|";
        options.minChars = typeof(options.minChars) != 'undefined' ? options.minChars : 1;
        options.delay = typeof(options.delay) != 'undefined' ? options.delay : 400;
        options.matchCase = typeof(options.matchCase) != 'undefined' ? options.matchCase : 0;
        options.matchSubset = typeof(options.matchSubset) != 'undefined' ? options.matchSubset : 1;
        options.matchContains = typeof(options.matchContains) != 'undefined' ? options.matchContains : 0;
        options.cacheLength = typeof(options.cacheLength) != 'undefined' ? options.cacheLength : 1;
        options.mustMatch = typeof(options.mustMatch) != 'undefined' ? options.mustMatch : 0;
        options.extraParams = typeof(options.extraParams) != 'undefined' ? options.extraParams : {};
        options.loadingClass = typeof(options.loadingClass) != 'undefined' ? options.loadingClass : "ac_loading";
        options.selectFirst = typeof(options.selectFirst) != 'undefined' ? options.selectFirst : false;
        options.selectOnly = typeof(options.selectOnly) != 'undefined' ? options.selectOnly : false;
        options.maxItemsToShow = typeof(options.maxItemsToShow) != 'undefined' ? options.maxItemsToShow : -1;
        options.autoFill = typeof(options.autoFill) != 'undefined' ? options.autoFill : false;
        options.width = typeof(options.width) != 'undefined' ? parseInt(options.width, 10) : 0;

        this.each(function() {
            var input = this;
            new jQuery.autocomplete(input, options);
        });

        // Don't break the chain
        return this;
    }
  • Excellent work! Exactly what I was searching for!! Is there a link where I can also download the autocomplete_ajax.cfm coldfusion page you're using?

    Thank you very much,
    Kevin
  • @Kevin:

    I've added a link at the bottom to download the autocomplete_ajax.cfm as a text file.

    It's pretty simple--you just want the script to spit out a pipe ("|") delimited list separated by new lines. Each line is converted into an array using the pipe delimiter.
  • I'm having some difficulty adding an extra parameter to the url I am calling. I figured out that the GET variable q is the text box value, but I need to pass an ID to the called URL as well. This is what I have so far:
    $("#FriendName").autocomplete(
    "autofill_friends2.php",
    {
    delay:10,
    minChars:2,
    matchSubset:1,
    matchContains:1,
    cacheLength:10,
    onItemSelect:selectItem,
    onFindValue:findValue,
    formatItem:formatItem,
    autoFill:true
    }
    );
  • @Todd:

    You can use the extraParams option to pass in addition URL parameters to pass.

    So to pass an ID of 123456, you do the following:

    $("#FriendName").autocomplete(
        "autofill_friends2.php",
        {
            delay:10,
            minChars:2,
            matchSubset:1,
            matchContains:1,
            cacheLength:10,
            onItemSelect:selectItem,
            onFindValue:findValue,
            formatItem:formatItem,
            autoFill:true,
            extraParams: {"id": "123456"}
        }
    );
  • How do the db calls work once a connection is opened? For instance, if I set the minChars:2 and type in Jo, it queries all cities that start with Jo. But what happens as I contitue to type? Is Joh a new db call? And then Johns, another call, and so on? Or is the initial call stored in chache?
  • @Randy,

    I sent this reponse to your jQuery mailing list inquiry:
    http://groups.google.com/group/jquery-en/browse_fr...
  • This a great plugin, but I have some problems when the GET response contains special characters (ex. Danish letters OE 0xd8)
    These are replaced by ? (question mark) in the list and in the input field. I've tried to set the response to charset ISO8859-1 with no luck. The $("#myInput").val("\xd8"); does display the right character.

    Any ideas?
  • @Jens:

    I know there is a problem in the makeUrl() when sending special characters that can be fixed with:

    function makeUrl(q) {
        var url = options.url + "?q=" + encodeURI(q);
        for (var i in options.extraParams) {
            url += "&" + i + "=" + encodeURI(options.extraParams[i]);
        }
        return url;
    };

    If this doesn't fix the issue, make sure that both the page where the page where the Autocomplete plugin is and the response page both return the same charset. I'd recommend using UTF-8, just because IE tends to handle special characters better when you use that charset.
  • Hi Dan, thanks for this great plugin. I'm having difficulties setting up this plugin for email address autocompletion and I was wondering if you could kindly help.

    I'd like to give focus back to the input field and I want the plugin to be able to handle multiple entries (more or less like http://just-tech.blogspot.com/2006/12/jquery-tweak...">anjesh's tweak). How can I achieve these?

    Thank you Dan.
  • @Michael:

    This version does not support multiple values.

    There is an autocomplete plug-in that's been in the works that does:

    http://jqueryjs.googlecode.com/svn/trunk/plugins/a...

    There are still issues that are being worked on--such as the autocomplete suggestions only work on the last entry (which means you don't get the suggestions if you're editing a 1st or 2nd values if you have 3 values in the field.)

    For support for this plug-in, I suggest you post messages to the jQuery Discussion list.
  • Dan, thanks much the info. I didn't aware there is an integration effort by Jörn.

    Just to be sure, by mentioning the list you mean General Discussion list (http://groups.google.com/group/jquery-en), right? I've hit 404 when I tried to go to the jQuery Plugins List (http://jquery.com/mailman/listinfo/plugins_jquery....).
  • @Michael:

    Yes I was referring to the General Discussion List:

    http://groups.google.com/group/jquery-en
  • Hi. Thanks for this great plugin!
    help with the below issue plz.

    $("#location").autocompleteArray(locs);
    $("#country").change( function(){
            $.get("http://apps.duppi.com/ajax/", {ac: "gl", cc: $(this).fieldValue()}, function(data){
    $("#location").autocompleteArray(data);
    )};

    I am trying to change suggestions array on #locations input on change of country SELECT element
  • @Chez:

    In the current source code, there's no way to update array. Invoking the autocompleteArray() a second time on a field will create a 2nd instance of the plugin on the field--which will cause problems.

    You could add a method to update the array (you'll also want to make sure that you call the flushCache() method as well to clear) or you could use the latest mod:

    http://dev.jquery.com/browser/trunk/plugins/autoco...

    The version at the URL above was re-written by Jörn, with some assistance from myself.

    It has a setOption() method which would allow you to update the data key with some new array data.
  • @Jens and Dan

    I think that Jens had the same issue as me and it was not solved with the above fix. The problem occurs with the returned data. If your server side script for instance returns:

    España
    Danmark
    København

    It would be displayed as:

    Espa?a
    Danmark
    K?benhavn

    The solution is as follows:

    In your server side script encode the output with a function that encodes a string as a URL rawurlencode() if you use php.

    Then change the following in autocomplete:

    In the function dataToDom change the following lines so they have the unescape() function around them:

    if (options.formatItem) {
     li.innerHTML = unescape(options.formatItem(row, i, num));
     li.selectValue = unescape(row[0]);
    } else {
     li.innerHTML = unescape(row[0]);
     li.selectValue = unescape(row[0]);
    }

    If you use the autoFill you also need to unescape that like so:

    $input.val($input.val() + unescape(sValue.substring(prev.length)));
  • I'm a bit new with JQuery - if I want the value of a hidden form field to be updated with the value of the selected choice, what would be the code I need to use. Thanks in advance.

    For example, if Woodstock is selected in the Ajax form, I want to populate the hidden form field with 1059.

    Thanks in advance -
    Stephen
  • Stephen,

    Just look at the code for the example of the "Ajax City Autocomplete". However instead of the following line in the findValue() function:

    alert("The value you selected was: " + sValue);

    You'd do something like:

    $("#hiddenFieldId").val(sValue);

    However, you may want to look into the updated Autocomplete code found in the jQuery SVN:

    http://dev.jquery.com/browser/trunk/plugins/autoco...
  • Dan, thank you very much for this code.

    I've set it up in my project and it works fine when testing with Firefox. But Internet Explorer refuses to even load the page. Just says "Operation aborted".

    Any ideas?
  • Oops. Okay that was solved easily by putting the autocomplete() call in a function. I'm a newbie to jquery... :)

    $(function(){
     $('thebox').autocomplete("blah.py");
    });
  • While that worked, it still does not seem to activate on load... I need to tab out to another field, tab back and then it automatically starts working. But when the page loads, and the autocomplete field gets focus, the loading gif shows up but there are no results showing... Any ideas?
  • For help, I'd recommend signing up for the jQuery mailing list:

    http://groups.google.com/group/jquery-en

    I subscribe to the list and there are many others on the list that can help you out.
  • Hi, nice script.

    I was wondering if this plugin supported matching ala textmate. That is, as long as the input characters are in the right order, a match is true even if there are skpped characters. For example, if the user had type 'jm', the following would match:

    12345 Jim
    John Mann
    4567 Ajax Money

    Thanks
  • Just a modification for this great autocomplete plugin to avoid the warning about "secured and unsecured element" with SSL in IE6. This is due to a problem with the iframe. You just have to add a src attribute on the iframe element with the location "javascript:false" in the JS.

    Before:
    if($.browser.msie){
      $results.append(document.createElement('iframe'));
    }

    After:
    if($.browser.msie){
      var iframe = document.createElement('iframe');
      iframe.src="javascript:false";
      $results.append(iframe);
    }

    Thanks
  • Is there any way to add additional properties when using a local array similar to what you can do with the Ajax calls and the '|' character? I'm making a menu type object, and using only the text inside the links for the autocomplete function to search. I need it to respond with the entire link though so that it is clickable.
  • @Jim:

    Just pass in an array of arrays instead of an array of strings. This will operate just like using th pipe symbol (|) to delimit your AJAX calls.
  • Great plugin Dan! I would using it for a chained menu, but...

    I don't get the result menu after passing a value to a second autocomplete input like #autocomplete2

    At #autocomplete1 I use onItemSelect:getFieldValue,

    function getFieldValue(li) {
      if( !!li.extra ) {
       var sValue = li.extra[0];
       $("#autocomplete1_hidden").val(sValue);
       $("#autocomplete2").val(sValue);
       $("#autocomplete2").findValue(); // not correct I think
      }
    }

    Only when I'm using my left cursor at #autocomplete2, then I get results.

    Please help!
  • @Jacco:

    I recommend you upgrade to the "official" version of the plug-in:
    http://dev.jquery.com/browser/trunk/plugins/autoco...
  • For some reason my code doesn't fire onItemSelect function. Could you please help me?

    Following is the code.

        $("#control").autocomplete
        (
          url,
          {
            extraParams: { propertyoid : property },
            delay: 750,
            width: 260,
            onItemSelect:function(li)
            {
              alert(123);
            },
            formatItem:function(data, i, total)
            {
              return data[0];
            },
            formatResult:function(data, i, total)
            {
              return data[0];
            }
          }
        );
  • Hi Dan,
    Great plug-in!
    One question: how can i programmatically (through some js call) switch the functionality off? is this at all possible? i am trying to add a "turn autocomplete off" functionality to my form, but no matter what i try (removing all added attributes/classes) the autocomplete persists...
    is there some switch in the function i am totally missing or is this functionality not there and once an autocomplete is created it can't be destroyed?

    Thanks!
  • @Azadi:

    There is no way to completely destroy it--at least without modifying the code. You could delete the input element and re-create it, which would remove the behavior.
  • Hi Dan,
    thanks for this plugin.

    I have one question: I can't figure out how to assign an "extra" value to the listed items when using the autocompleteArray function.

    For example, I tried using this type of array
    ["Allen|1","Albert|2","Alberto|3","Alladin|4"]
    specifying cellSeparator:'|' in the options but the id's are shown in the items instead of being used as extra.

    What am I doing wrong?
    Thanks in advance for your help Dan.
    JB
  • Dear Dan,
    don't take my request into account, I just read that you already answered the same question to someonelse in the comments above.
    For next readers, an array of array is:
    [ ["Allen",1]
     ,["Albert",2]
     ,["Alberto",1]
     ,["Alladin",1] ]

    Thanks
  • Hi Dan, great plugin. It works very well and has a much smaller footprint than some of the alternatives.

    One bug, though -- I have a foo.php which searches a MySQL DB and returns strings as follows, e.g. if I type "Denv" it returns "Denver Business Journal", "Denver Post", etc. This works great. However, the text inside the box reads "DenvDenver". This seems like a bug.

    What did I do wrong? This behavior doesn't manifest on your example site.
  • Sorry for my hasty post -- I see what's going on now. Your 'autofill' function assumes that the first match is the best, but if the first match contains a substring and therefore doesn't start at position zero, the autofill doesn't line up. I can probably mess with the autofill function to do what I want, unless there's something better you can recommend. I do want to keep "matchSubset" set to "1".
  • This is a great plug in. I'm struggling though to figure out how to change the array based on event from drop down. Like chez. I'm a newbie to jquery. Can someone help me out? I can successfully use a variable for the array, but when I try to change it the browser blows up. The part that's blowing up is when I call the autoCompleteArray from the event. Here is what I have:

      function myevent(selection)
      {
        if (selection == "1") { pageQuartersArray = standardYears; }
        if (selection == "2") { pageQuartersArray = referenceYears; }
        if (selection == "3") { pageQuartersArray = referenceQuarters; }

        $("#ctl00_MasterContentPlace_txtQtr1").autocompleteArray(pageQuartersArray,
          {
              delay:10,
              minChars:1,
              matchSubset:1,
              onItemSelect:selectItem,
              onFindValue:findValue,
              autoFill:true,
              maxItemsToShow:10
          }
        );
      }

      var referenceQuarters = ["0Q","-1Q","-2Q"];
      var referenceYears = ["0Y","-1Y","-2Y","-3Y"];
      var standardYears = ["2008Q1","2007Q4","2007Q3"];
      var pageQuartersArray = ["2008Q1","2007Q4","2007Q3"];

      $("#ctl00_MasterContentPlace_txtQtr1").autocompleteArray(pageQuartersArray,
        {
            delay:10,
            minChars:1,
            matchSubset:1,
            onItemSelect:selectItem,
            onFindValue:findValue,
            autoFill:true,
            maxItemsToShow:10
        }
      );
  • I should say the first call to autocompleteArray is resides within a   $(document).ready(function(){
        }

    thanks
  • @Justin:

    I recommend you upgrade to the "official" version of the plug-in:
    http://dev.jquery.com/browser/trunk/plugins/autoco...

    It offers ways to flush the cache and update the array. It's also the "official" UI version.
  • Thanks for the fast response Dan. I will do that.
  • Hi,

    I was wondering if you have an example PHP script for getting the autocomplete list items from a MySQL database.

    best,
  • como hago para que el autocompletar funcione sin almacenar datos en cache, cuando lo utilizo por primera vez me trae los datos bien con un select a la BD, pero cuando lo utilizo por segunda vez con otro select a la BD que no me trar informacion, este autocompletar me sigue mostrando los resultados como si yo mandara el primer select. como lo soluciono, como borro la caché despues de traer los datos, o como configuro para que no muestre nada si la consulta a la base de datos no tiene informacion???????
  • Hi
    I don't know why, by onItemSelect doesn't work i my code:
    <code>
    function getFieldValue(li) {
          if( !!li.extra ) {
                var sValue = li.extra[0];
                alert(sValue);
          }
      }

    $("#customer").autocomplete(
          "admin/person_clients/autocomplete",
          {
            delay:5,
            minChars:1,
            matchSubset:1,
            matchContains:1,
            formatItem:formatItem,
            onItemSelect:getFieldValue,
            autoFill:true
          }
       );
    </code>
    formatItem work's.
    any ideas?
  • Hey, great Plug -in !!! Really usefull

    But i have problems with the encoding / charset. My database is in windows 1251. But it only showa "?????".
    Any idea how to fix this? I read other suggestions but it doesn't really work.
    Thanks in advanced!
  • @JC:

    Sounds like your server-side scripting language isn't encoding the page correctly.
  • Hey i love the work. One question i have is, is there a way to bold or highlight the entered characters in the form field and in the results list?


    Regards
  • @Steve:

    I highly recommend using the official AC plug-in:
    http://bassistance.de/jquery-plugins/jquery-plugin...

    It has the features you want.
  • Hi Dan,

    You seem to know a lot about this AutoComplete add in. I wonder if you can help with the following issue.

    From a Popup, which is a form in an IFrame, the list of selections array in an AutoComplete modified input tag on the parent form is modified. The input tag is reprocessed with Autocomplete

    i.e. parent.$("#InputTagName").AutoComplete(parent.ArrayName, {})

    The AutoComplete code creates a new cache and sets the correct matched sets based on the modified array but when the popup is closed only the old list is present in the parent form input tag.

    I guess the new cache is associated with the current form even though the modified input tag is on the parent form.

    This sounds impossible, at least with my level of understanding of Javascript, but can the parent form AutoComplete cache be modified from the child Iframe(d) form?
  • @Rob:

    My mod does not offer a way to flush the cache. The official AC plug-in that comes with the jQuery UI library I believe does--and even if it doesn't provide a method for flushing the cache, it provides ways to destroy the old behavior so you can re-attach the new behavior.

    So, go grab the official AC plug-in and use that instead.
  • Thanks for that Dan,

    I did consider changing to the UI AC but felt that it might not be any better at implementing this requirement.

    I'll try it..

Comments for this entry have been disabled.